﻿using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Runtime.InteropServices;
using System.Xml;
using Assist;
using Autodesk.Revit.DB;
using Autodesk.Revit.DB.Architecture;
using Autodesk.Revit.DB.ExtensibleStorage;
using Autodesk.Revit.UI;
using Autodesk.Revit.UI.Selection;
using YArchitech.Controls;
using YArchitech.LIB;
using YArchitech.Revit;

namespace YJKArch.NumberRoom
{
	public class NumberRoomsControler
	{
		public NumberRoomsControler(ExternalCommandData cmdData)
		{
			this.m_Revit = cmdData;
		}

		[DllImport("user32.dll", EntryPoint = "SendMessageA")]
		public static extern int SendMessage(IntPtr hwnd, int wMsg, IntPtr wParam, IntPtr lParam);

		public Result Create(ref string message)
		{
			DlgRoomNumber dlgRoomNumber = new DlgRoomNumber(this.m_Revit);
			dlgRoomNumber.Show(CmdNumberRooms._hWndRevit);
			int wMsg = 7;
			NumberRoomsControler.SendMessage(CmdNumberRooms._hWndRevit.Handle, wMsg, IntPtr.Zero, IntPtr.Zero);
            Result result = Result.Cancelled;
			this.InitialRoomNumberSettings();
			try
			{
				for (;;)
				{
					switch (dlgRoomNumber.CurrentOperateType)
					{
					case DlgRoomNumber.OperateType.OT_Pick:
						result = this.DoPickNumberRoom(dlgRoomNumber);
						break;
					case DlgRoomNumber.OperateType.OT_Continue:
						result = this.DoVolumeNumber(dlgRoomNumber);
						break;
					case DlgRoomNumber.OperateType.OT_Change:
						result = this.DoChange(dlgRoomNumber);
						break;
					}
					if (result == Autodesk.Revit.UI.Result.Cancelled)
					{
						if (!dlgRoomNumber.PickButton)
						{
							break;
						}
						dlgRoomNumber.PickButton = false;
					}
					else if (result == Autodesk.Revit.UI.Result.Failed)
					{
						break;
					}
				}
			}
			catch (Exception ex)
			{
				dlgRoomNumber.Close();
				message = ex.Message;
				return Autodesk.Revit.UI.Result.Failed;
			}
			dlgRoomNumber.Close();
			return Autodesk.Revit.UI.Result.Succeeded;
		}

		private void InitialRoomNumberSettings()
		{
			string text = Product.UserDataLocation + "\\RoomNumberSettings.xml";
			if (File.Exists(text))
			{
				return;
			}
			XmlDocument xmlDocument = new XmlDocument();
			XmlNode newChild = xmlDocument.CreateXmlDeclaration("1.0", "gb2312", null);
			xmlDocument.AppendChild(newChild);
			XmlElement xmlElement = xmlDocument.CreateElement("RoomNumberSettingsData");
			xmlDocument.AppendChild(xmlElement);
			XmlElement xmlElement2 = xmlDocument.CreateElement("Data");
			xmlElement.AppendChild(xmlElement2);
			XmlElement xmlElement3 = xmlDocument.CreateElement("房间名称");
			xmlElement3.InnerText = "True";
			xmlElement2.AppendChild(xmlElement3);
			xmlElement3 = xmlDocument.CreateElement("单元");
			xmlElement3.InnerText = "False";
			xmlElement2.AppendChild(xmlElement3);
			xmlElement3 = xmlDocument.CreateElement("房间编号");
			xmlElement3.InnerText = "False";
			xmlElement2.AppendChild(xmlElement3);
			xmlElement3 = xmlDocument.CreateElement("前缀");
			xmlElement3.InnerText = "";
			xmlElement2.AppendChild(xmlElement3);
			xmlElement3 = xmlDocument.CreateElement("后缀");
			xmlElement3.InnerText = "";
			xmlElement2.AppendChild(xmlElement3);
			xmlElement3 = xmlDocument.CreateElement("房间面积");
			xmlElement3.InnerText = "True";
			xmlElement2.AppendChild(xmlElement3);
			xmlElement3 = xmlDocument.CreateElement("方式1");
			xmlElement3.InnerText = "False";
			xmlElement2.AppendChild(xmlElement3);
			xmlElement3 = xmlDocument.CreateElement("方式2");
			xmlElement3.InnerText = "False";
			xmlElement2.AppendChild(xmlElement3);
			xmlElement3 = xmlDocument.CreateElement("自动更新");
			xmlElement3.InnerText = "False";
			xmlElement2.AppendChild(xmlElement3);
			xmlDocument.Save(text);
		}

		private Result DoPickNumberRoom(DlgRoomNumber dlg)
		{
			Autodesk.Revit.DB.Document document = this.m_Revit.Application.ActiveUIDocument.Document;
			Autodesk.Revit.DB.View activeView = this.m_Revit.Application.ActiveUIDocument.ActiveView;
			Transaction transaction = null;
			XYZ pt = null;
			try
			{
				transaction = new Transaction(document);
				transaction.Start("create room tag");
				while (this.SelelctPosision(ref pt))
				{
					dlg.GetDimStrings();
					RoomTagType roomTagType = RoomNumberDataOperator.GetRoomTagType(this.m_Revit, !dlg.bnForShareParameter.sEngName.IsEmpty<char>(), dlg.bnForShareParameter.bDrawApart);
					if (dlg.RoomNumberValidating())
					{
						Room room = null;
						if (this.GetRoomByPoint(pt, true, ref room))
						{
							this.SetParametersValue(room, dlg.CurContinueNum, dlg.bnForShareParameter);
							string sDName = dlg.bnForShareParameter.sDName;
							RoomTag roomTag = this.CreateRoomTag(document, activeView, room, pt, sDName);
							if (roomTagType != null)
							{
								roomTag.RoomTagType = roomTagType;
							}
							if (string.IsNullOrEmpty(dlg.bnForShareParameter.strPopDensity))
							{
								this.WriteExtendDataToRoom(room, 0.0);
							}
							else
							{
								this.WriteExtendDataToRoom(room, Convert.ToDouble(dlg.bnForShareParameter.strPopDensity));
							}
							Common.UpdateModel(this.m_Revit.Application.ActiveUIDocument, false);
							NumberRoomsControler.SendMessage(dlg.Handle, 1281, IntPtr.Zero, IntPtr.Zero);
						}
					}
				}
				transaction.Commit();
			}
			catch (Exception ex)
			{
				string message = ex.Message;
				dlg.Close();
				if (transaction != null && transaction.HasStarted() && !transaction.HasEnded())
				{
					transaction.RollBack();
				}
				return Autodesk.Revit.UI.Result.Failed;
			}
			return Autodesk.Revit.UI.Result.Cancelled;
		}

		protected bool SelelctPosision(ref XYZ pt)
		{
			try
			{
				pt = this.m_Revit.Application.ActiveUIDocument.Selection.PickPoint("选取房间标记位置");
			}
			catch (Exception)
			{
				return false;
			}
			return true;
		}

		private bool GetRoomByPoint(XYZ pt, bool createRoom, ref Room selectRoom)
		{
			double num = AssistFunc.mmToFeet(10.0);
			num *= num;
			List<ElementId> list = new List<ElementId>();
			Autodesk.Revit.DB.Document document = this.m_Revit.Application.ActiveUIDocument.Document;
			Level genLevel = this.m_Revit.Application.ActiveUIDocument.ActiveView.GenLevel;
			foreach (object obj in document.get_PlanTopology(genLevel).Circuits)
			{
				PlanCircuit planCircuit = (PlanCircuit)obj;
				if (!planCircuit.IsRoomLocated && !this.LessThan(planCircuit.Area, num) && createRoom)
				{
					Room room2 = document.Create.NewRoom(null, planCircuit);
					list.Add(room2.Id);
				}
			}
			bool result = false;
            List<Room> rooms = YJKArch.NumberRoom.Common.GetRooms(document, this.m_Revit.Application.ActiveUIDocument.ActiveView.Id);
			XYZ xyz = new XYZ(pt.X, pt.Y, this.m_Revit.Application.ActiveUIDocument.ActiveView.GenLevel.Elevation);
			using (List<Room>.Enumerator enumerator2 = rooms.GetEnumerator())
			{
				while (enumerator2.MoveNext())
				{
					Room room = enumerator2.Current;
					if (room.IsPointInRoom(xyz))
					{
						int num2 = list.FindIndex((ElementId id) => id.Compare(room.Id) == 0);
						if (num2 < 0)
						{
							selectRoom = room;
						}
						else if (createRoom)
						{
							list.RemoveAt(num2);
							selectRoom = room;
						}
						result = true;
						break;
					}
				}
			}
			foreach (ElementId elementId in list)
			{
				document.Delete(elementId);
			}
			return result;
		}

		private bool PickRooms(ref List<Element> roomList)
		{
			Selection selection = this.m_Revit.Application.ActiveUIDocument.Selection;
			try
			{
				YJKArch.AreaSet.RoomFilter  roomFilter = new YJKArch.AreaSet.RoomFilter ();
				foreach (Element element in selection.PickElementsByRectangle(roomFilter, "请框选房间"))
				{
					Room item = element as Room;
					roomList.Add(item);
				}
			}
			catch (Exception)
			{
				return false;
			}
			return true;
		}

		private Result DoVolumeNumber(DlgRoomNumber dlg)
		{
			Autodesk.Revit.DB.Document document = this.m_Revit.Application.ActiveUIDocument.Document;
			Autodesk.Revit.DB.View activeView = this.m_Revit.Application.ActiveUIDocument.ActiveView;
			Transaction transaction = new Transaction(document);
			try
			{
				List<Element> list = new List<Element>();
				while (this.PickRooms(ref list))
				{
					if (dlg.RoomNumberValidating())
					{
						transaction = new Transaction(document);
						transaction.Start("volumeNumberRoom");
						this.NumberRooms(dlg, list, 0, 1);
						transaction.Commit();
						list.Clear();
					}
				}
			}
			catch (Exception)
			{
				if (transaction != null && transaction.HasStarted() && !transaction.HasEnded())
				{
					transaction.RollBack();
				}
				return Autodesk.Revit.UI.Result.Failed;
			}
			return Autodesk.Revit.UI.Result.Cancelled;
		}

		private VolumeNumberControler.ResultType NumberRooms(DlgRoomNumber dlg, List<Element> roomList, int angle, int sortWay)
		{
			Autodesk.Revit.DB.Document document = this.m_Revit.Application.ActiveUIDocument.Document;
			Autodesk.Revit.DB.View activeView = this.m_Revit.Application.ActiveUIDocument.ActiveView;
			double num = (double)angle * Math.PI / 180.0;
			List<NumberRoomsControler.RoomPositionInfo> list = new List<NumberRoomsControler.RoomPositionInfo>();
			foreach (Element element in roomList)
			{
				Room room = element as Room;
				if (room != null)
				{
					List<ElementId> tagsOfRoom = Common.GetTagsOfRoom(document, activeView.Id, room);
					if (tagsOfRoom.Count > 0)
					{
						foreach (ElementId elementId in tagsOfRoom)
						{
							document.Delete(elementId);
						}
					}
					Location location = room.Location;
					XYZ refPoint = this.GetRefPoint(room);
					XYZ transformPoint = this.GetTransformPoint(refPoint, -num, XYZ.BasisZ, (NumberRoomsControler.SortWay)sortWay);
					list.Add(new NumberRoomsControler.RoomPositionInfo
					{
						position = transformPoint,
						room = room
					});
				}
			}
			list.Sort((NumberRoomsControler.RoomPositionInfo left, NumberRoomsControler.RoomPositionInfo right) => XYComparer.Compare(left.position, right.position));
			RoomTagType roomTagType = RoomNumberDataOperator.GetRoomTagType(this.m_Revit, !dlg.bnForShareParameter.sEngName.IsEmpty<char>(), dlg.bnForShareParameter.bDrawApart);
			foreach (NumberRoomsControler.RoomPositionInfo roomPositionInfo in list)
			{
				this.SetParametersValue(roomPositionInfo.room, dlg.CurContinueNum, dlg.bnForShareParameter);
				string sDName = dlg.bnForShareParameter.sDName;
				XYZ point = (roomPositionInfo.room.Location as LocationPoint).Point;
				RoomTag roomTag = this.CreateRoomTag(document, activeView, roomPositionInfo.room, point, sDName);
				if (roomTagType != null)
				{
					roomTag.RoomTagType = roomTagType;
				}
				dlg.CurContinueNum++;
			}
			return VolumeNumberControler.ResultType.RT_OK;
		}

		private XYZ GetRefPoint(Room room)
		{
			Autodesk.Revit.DB.Document document = this.m_Revit.Application.ActiveUIDocument.Document;
			SpatialElementBoundaryOptions spatialElementBoundaryOptions = new SpatialElementBoundaryOptions();
			IEnumerable<IList<BoundarySegment>> boundarySegments = room.GetBoundarySegments(spatialElementBoundaryOptions);
			List<XYZ> list = new List<XYZ>();
			foreach (IList<BoundarySegment> list2 in boundarySegments)
			{
				foreach (BoundarySegment segment in list2)
				{
					segment.GetElement(document);
					Curve curve = segment.GetCurve();
					list.Add(curve.GetEndPoint(0));
					list.Add(curve.GetEndPoint(1));
				}
			}
			list.Sort((XYZ left, XYZ right) => XYComparer.Compare(left, right));
			return list[0];
		}

		private XYZ TransformPoint(XYZ point, Transform transform)
		{
			double x = point.X;
			double y = point.Y;
			double z = point.Z;
			XYZ xyz = transform.get_Basis(0);
			XYZ xyz2 = transform.get_Basis(1);
			XYZ xyz3 = transform.get_Basis(2);
			XYZ origin = transform.Origin;
			double num = x * xyz.X + y * xyz2.X + z * xyz3.X + origin.X;
			double num2 = x * xyz.Y + y * xyz2.Y + z * xyz3.Y + origin.Y;
			double num3 = x * xyz.Z + y * xyz2.Z + z * xyz3.Z + origin.Z;
			return new XYZ(num, num2, num3);
		}

		private XYZ RotateTo(XYZ oldVector, double angle, XYZ axis)
		{
			Transform transform = RevitVersionFuncs.CreateRotationAtPoint(axis, angle, new XYZ(0.0, 0.0, 0.0));
			return this.TransformPoint(oldVector, transform);
		}

		private XYZ GetTransformPoint(XYZ oldVector, double angle, XYZ axis, NumberRoomsControler.SortWay sortWay)
		{
			XYZ xyz = this.RotateTo(oldVector, angle, axis);
			XYZ result = xyz;
			switch (sortWay)
			{
			case NumberRoomsControler.SortWay.SW_RightBottom:
				result = new XYZ(xyz.X, -xyz.Y, xyz.Z);
				break;
			case NumberRoomsControler.SortWay.SW_LeftTop:
				result = new XYZ(-xyz.X, xyz.Y, xyz.Z);
				break;
			case NumberRoomsControler.SortWay.SW_LeftBottom:
				result = new XYZ(-xyz.X, -xyz.Y, xyz.Z);
				break;
			case NumberRoomsControler.SortWay.SW_TopRight:
				result = new XYZ(xyz.Y, xyz.X, xyz.Z);
				break;
			case NumberRoomsControler.SortWay.SW_TopLeft:
				result = new XYZ(xyz.Y, -xyz.X, xyz.Z);
				break;
			case NumberRoomsControler.SortWay.SW_BottomRight:
				result = new XYZ(-xyz.Y, xyz.X, xyz.Z);
				break;
			case NumberRoomsControler.SortWay.SW_BottomLeft:
				result = new XYZ(-xyz.Y, -xyz.X, xyz.Z);
				break;
			}
			return result;
		}

		private Result DoChange(DlgRoomNumber dlg)
		{
			Autodesk.Revit.DB.Document document = this.m_Revit.Application.ActiveUIDocument.Document;
			Autodesk.Revit.DB.View activeView = this.m_Revit.Application.ActiveUIDocument.ActiveView;
			Transaction transaction = new Transaction(document);
			try
			{
				List<Element> list = new List<Element>();
				while (this.PickRooms(ref list))
				{
					transaction = new Transaction(document);
					transaction.Start("changeRoom");
					foreach (Element element in list)
					{
						Room room = element as Room;
						if (room != null)
						{
							this.SetParametersValue(room, dlg.bnForShareParameter);
						}
					}
					transaction.Commit();
					YJKMessageBox.Show("已更改" + list.Count.ToString() + "个所选房间的户型！");
					list.Clear();
				}
			}
			catch (Exception)
			{
				if (transaction != null && transaction.HasStarted() && !transaction.HasEnded())
				{
					transaction.RollBack();
				}
				return Autodesk.Revit.UI.Result.Failed;
			}
			return Autodesk.Revit.UI.Result.Cancelled;
		}

		private RoomTag CreateRoomTag(Autodesk.Revit.DB.Document doc, Autodesk.Revit.DB.View view, Room room, XYZ pt, string tagName)
		{
			XYZ point = (room.Location as LocationPoint).Point;
			UV point2 = new UV(point.X, point.Y);
			List<ElementId> tagsOfRoom = Common.GetTagsOfRoom(doc, view.Id, room);
			if (tagsOfRoom.Count > 0)
			{
				foreach (ElementId elementId in tagsOfRoom)
				{
					doc.Delete(elementId);
				}
			}
			this.SetRoomName(room, tagName);
			return RevitVersionFuncs.NewRoomTag(doc, room, point2, view);
		}

		private void SetRoomName(Room room, string strName)
		{
			room.get_Parameter(BuiltInParameter.ROOM_NAME).Set(strName);
		}

		private bool LessThan(double val1, double val2)
		{
			return val1 - val2 < -1E-09;
		}

		protected bool SetParametersValue(Element element, int nRoomNum, ButtonNode bn)
		{
			if (element == null)
			{
				return false;
			}
			bool result;
			try
			{
				ElementParameter.SetParameterValueEx(element, "换行文字", bn.sEngName, (Autodesk.Revit.DB.ParameterType)1);
				ElementParameter.SetParameterValueEx(element, "房间编号", nRoomNum.ToString(), (Autodesk.Revit.DB.ParameterType)1);
				ElementParameter.SetParameterValueEx(element, "面积系数", bn.dAreaCoefficient.ToString(), (Autodesk.Revit.DB.ParameterType)1);
				ElementParameter.SetParameterValueEx(element, "户型名称", bn.sApartName, (Autodesk.Revit.DB.ParameterType)1);
				ElementParameter.SetParameterValueEx(element, "户型编号", bn.sApartNumber, (Autodesk.Revit.DB.ParameterType)1);
				ElementParameter.SetParameterValueEx(element, "单元", bn.sUnit, (Autodesk.Revit.DB.ParameterType)1);
				ElementParameter.SetParameterValueEx(element, "楼号", bn.sBuildingNumber, (Autodesk.Revit.DB.ParameterType)1);
				ElementParameter.SetParameterValueEx(element, "人员密度", bn.strPopDensity, (Autodesk.Revit.DB.ParameterType)1);
				ElementParameter.SetParameterValueEx(element, "前缀", RoomNumberDataOperator.GetPrefixString(), (Autodesk.Revit.DB.ParameterType)1);
				ElementParameter.SetParameterValueEx(element, "后缀", RoomNumberDataOperator.GetSuffixString(), (Autodesk.Revit.DB.ParameterType)1);
				if (RoomNumberDataOperator.GetDimAreaByTextOrNot())
				{
					ElementParameter.SetParameterValueEx(element, "房间编号面积单位", "平方米", (Autodesk.Revit.DB.ParameterType)1);
				}
				else
				{
					ElementParameter.SetParameterValueEx(element, "房间编号面积单位", "㎡", (Autodesk.Revit.DB.ParameterType)1);
				}
				result = true;
			}
			catch (Exception)
			{
				result = false;
			}
			return result;
		}

		protected bool SetParametersValue(Element element, ButtonNode bn)
		{
			if (element == null)
			{
				return false;
			}
			bool result;
			try
			{
				ElementParameter.SetParameterValueEx(element, "户型名称", bn.sApartName, (Autodesk.Revit.DB.ParameterType)1);
				ElementParameter.SetParameterValueEx(element, "户型编号", bn.sApartNumber, (Autodesk.Revit.DB.ParameterType)1);
				result = true;
			}
			catch (Exception)
			{
				result = false;
			}
			return result;
		}

		private void WriteExtendDataToRoom(Room room, double dPd)
		{
			if (room == null)
			{
				return;
			}
			Guid guid = new Guid("f1e15105-8311-4a71-b48b-ec64c2d28380");
			Schema schema = Schema.Lookup(guid);
			if (schema == null)
			{
				SchemaBuilder schemaBuilder = new SchemaBuilder(guid);
				schemaBuilder.SetSchemaName("RoomPDExData");
				schemaBuilder.AddSimpleField("PopDensity", typeof(double)).SetUnitType(0);
				schema = schemaBuilder.Finish();
			}
			Entity entity = new Entity(schema);
			Field field = schema.GetField("PopDensity");
			entity.Set<double>(field, dPd, (Autodesk.Revit.DB.DisplayUnitType)6);
			room.SetEntity(entity);
		}

		private const string PDROOM_GUID = "f1e15105-8311-4a71-b48b-ec64c2d28380";

		private const string m_SchemaName = "RoomPDExData";

		private const string m_strPopDensity = "PopDensity";

		private ExternalCommandData m_Revit;

		public const int USER = 1280;

		public const int WM_MYMESSAGE = 1281;

		private const double _epsDouble = 1E-09;

		private struct NumberRoomsInfo
		{
			public int number;

			public Room room;
		}

		private struct RoomPositionInfo
		{
			public XYZ position;

			public Room room;
		}

		private enum SortWay
		{
			SW_RightTop,
			SW_RightBottom,
			SW_LeftTop,
			SW_LeftBottom,
			SW_TopRight,
			SW_TopLeft,
			SW_BottomRight,
			SW_BottomLeft
		}

		public enum ResultType
		{
			RT_OK,
			RT_Cancel,
			RT_Exception
		}
	}
}
